﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;

namespace cmsservice.Common
{
    public class ConnectionPool
    {
        private static System.Timers.Timer timer = new System.Timers.Timer();
        static ConnectionPool()
        {
            timer.Interval = Keep_Alive * 1000;
            timer.Elapsed += new System.Timers.ElapsedEventHandler(ClearOldCon);
            timer.Start();
        }

        private static void ClearOldCon(object sender, EventArgs e)
        {
            lock (dicCon)
            {
                foreach (var key in dicCon.Keys)
                {
                    var temp = dicCon[key];
                    for (int i = temp.Lst.Count - 1; i >= 0; i--)
                    {
                        var c = temp.Lst[i];
                        if (c.State == State.free && c.UpdateTime < DateTime.Now.AddSeconds(-Keep_Alive))
                        {
                            try
                            {
                                temp.Lst[i].CloseSocket();
                            }
                            catch
                            {
                            }
                            temp.Lst.RemoveAt(i);
                        }
                    }
                }
            }
        }

        private static int Keep_Alive = 30;

        private static Dictionary<string, ConnectionCollection> dicCon = new Dictionary<string, ConnectionCollection>();

        public static Dictionary<string, ConnectionCollection> DicCon
        {
            get
            {
                return dicCon;
            }
        }


        private static Mutex m_mutex = new Mutex();

        public static Connection GetConnection(string ip, int port)
        {
            try
            {
                m_mutex.WaitOne();
                lock (dicCon)
                {
                    string key = ip + "_" + port;
                    Connection con = null;
                    if (dicCon.ContainsKey(key))
                    {
                        var conCollection = dicCon[key];
                        con = conCollection.GetConnection();

                    }
                    else
                    {
                        ConnectionCollection conCollection = new ConnectionCollection();
                        conCollection.Ip = ip;
                        conCollection.Port = port;
                        dicCon.Add(key, conCollection);
                        con = conCollection.GetConnection();
                    }
                    if (con != null)
                    {
                        con.UpdateTime = DateTime.Now;
                        con.State = State.busy;
                    }
                    return con;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                m_mutex.ReleaseMutex();
            }
        }

        public Connection getCon(string key, Connection con)
        {
            var tt = key.Split('_');
            IPAddress ipAddress = IPAddress.Parse(tt[0]);
            int port = int.Parse(tt[1]);
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
            // Create a TCP/IP socket.
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            // Connect to the remote endpoint.
            client.Connect(remoteEP);
            if (con == null)
                con = new Connection();
            con.State = State.free;
            con.SetSocket(client);
            return con;
        }
    }

    public class ConnectionCollection
    {
        private System.Timers.Timer timer = new System.Timers.Timer();
        int Max_Conntions = 10;

        public ConnectionCollection()
        {
            FlagHealth = true;
            Lst = new List<Connection>();
            timer.Interval = 5000;
            timer.Elapsed += new System.Timers.ElapsedEventHandler(CheckHealth);
            timer.Start();
        }

        private Socket GetSocket()
        {
            IPAddress ipAddress = IPAddress.Parse(Ip);
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, Port);
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                client.Connect(remoteEP);
                FlagHealth = true;
            }
            catch
            {
                FlagHealth = false;
            }
            return client;
        }

        public Connection GetConnection()
        {
            if (!FlagHealth)
            {
                return null;
            }
            lock (Lst)
            {
                for (int i = 0; i < Lst.Count; i++)
                {
                    var c = Lst[i];
                    if (c.State == State.free)
                    {
                        if (c.IsSocketed())
                        {
                            return c;
                        }
                        else
                        {
                            c.SetSocket( GetSocket());
                            return c;
                        }
                    }
                }
                while (true)
                {
                    var temp = Lst.Where(x => x.State == State.free).ToArray();
                    if (temp.Length > 0)
                    {
                        var c = temp[0];
                        if (c.IsSocketed() == true)
                        {
                            return c;
                        }
                        else
                        {
                            c.SetSocket(GetSocket());
                            return c;
                        }
                    }
                    if (Lst.Count < Max_Conntions)
                    {
                        Connection con = new Connection();
                        con.SetSocket(GetSocket());
                        con.State = State.free;
                        Lst.Add(con);
                        return con;
                    }
                    Thread.Sleep(50);
                }
            }
        }



        void CheckHealth(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(Ip) && Port != 0)
            {
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                try
                {
                    client.Connect(Ip, Port);
                    FlagHealth = true;
                }
                catch
                {
                    FlagHealth = false;
                }
                finally
                {
                    if (client != null)
                    {
                        try
                        {
                            client.Disconnect(false);
                            client.Close();
                        }
                        catch
                        {
                        }
                    }
                }
            }
        }


        public bool IsHealth
        {
            get
            {
                return FlagHealth;
            }
        }

        private bool FlagHealth
        {
            get;
            set;
        }

        public string Key
        {
            get
            {
                return Ip + "_" + Port;
            }
        }

        public string Ip
        {
            get;
            set;
        }

        public int Port
        {
            get;
            set;
        }

        public List<Connection> Lst
        {
            get;
            set;
        }


    }

    public class Connection
    {
        public Connection()
        {
            State = State.free; ;
        }



        public State State
        {
            get;
            set;
        }

        public int SendFailReset(byte[] buffer)
        {
            try
            {
                return Socket.Send(buffer);
            }
            catch
            {
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                client.Connect(Socket.RemoteEndPoint);
                Socket = client;
                return Socket.Send(buffer);
            }
        }

        public int Send(byte [] buffer)
        {
            return Socket.Send(buffer);
        }

        public int Receive(byte [] buffer)
        {
            return Socket.Receive(buffer);
        }

        public bool IsSocketed()
        {
            return Socket.Connected;
        }

        public void CloseSocket()
        {
             Socket.Disconnect(false);
             Socket.Close();
        }

        public void SetSocket(Socket socket)
        {
            Socket = socket;
        }

        private Socket Socket
        {
            get;
            set;
        }

        public DateTime UpdateTime
        {
            get;
            set;
        }

        public void Close()
        {
            State = State.free;
        }
    }

    public enum State
    {
        free,
        busy
    }
}